#pragma once
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>



// util.c
int readLine(const SOCKET socket, char* buffer, const int bufferSize);
long getFileSize(FILE *file);
long getFileSizeByPath(const char *filePath);
bool isFile(const char *filePath);
bool strEndsWith(const char *str, const char *suffix);
bool strStartsWith(const char *str, const char *prefix);
bool socketToFile(const SOCKET socket, FILE *file, unsigned long size);
bool lastModifyOfFile(const char* filePath, char* buffer, const size_t bufferSize);



// status.c
typedef enum {
    HTTP_CONTINUE = 100,
    HTTP_SWITCHING_PROTOCOLS = 101,
    HTTP_OK = 200,
    HTTP_CREATED = 201,
    HTTP_ACCEPTED = 202,
    HTTP_NON_AUTHORITATIVE_INFORMATION = 203,
    HTTP_NO_CONTENT = 204,
    HTTP_RESET_CONTENT = 205,
    HTTP_PARTIAL_CONTENT = 206,
    HTTP_MULTIPLE_CHOICES = 300,
    HTTP_MOVED_PERMANENTLY = 301,
    HTTP_FOUND = 302,
    HTTP_SEE_OTHER = 303,
    HTTP_NOT_MODIFIED = 304,
    HTTP_USE_PROXY = 305,
    HTTP_TEMPORARY_REDIRECT = 307,
    HTTP_BAD_REQUEST = 400,
    HTTP_UNAUTHORIZED = 401,
    HTTP_PAYMENT_REQUIRED = 402,
    HTTP_FORBIDDEN = 403,
    HTTP_NOT_FOUND = 404,
    HTTP_METHOD_NOT_ALLOWED = 405,
    HTTP_NOT_ACCEPTABLE = 406,
    HTTP_PROXY_AUTHENTICATION_REQUIRED = 407,
    HTTP_REQUEST_TIMEOUT = 408,
    HTTP_CONFLICT = 409,
    HTTP_GONE = 410,
    HTTP_LENGTH_REQUIRED = 411,
    HTTP_PRECONDITION_FAILED = 412,
    HTTP_REQUEST_ENTITY_TOO_LARGE = 413,
    HTTP_REQUEST_URI_TOO_LONG = 414,
    HTTP_UNSUPPORTED_MEDIA_TYPE = 415,
    HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
    HTTP_EXPECTATION_FAILED = 417,
    HTTP_INTERNAL_SERVER_ERROR = 500,
    HTTP_NOT_IMPLEMENTED = 501,
    HTTP_BAD_GATEWAY = 502,
    HTTP_SERVICE_UNAVAILABLE = 503,
    HTTP_GATEWAY_TIMEOUT = 504,
    HTTP_HTTP_VERSION_NOT_SUPPORTED = 505
} HTTPStatusCode;
const char *getHTTPStatusMessage(const HTTPStatusCode code);



// header.c
typedef struct {
    char *key;
    char *value;
} HTTPHeader;

typedef struct {
    HTTPHeader *data;
    unsigned long length;
    unsigned long capacity;
} HTTPHeaders;

HTTPHeaders newEmptyHeaders();
void freeHeaders(HTTPHeaders *headers);
HTTPHeader getHeaderByIdx(const HTTPHeaders *headers, const unsigned long index);
bool appendHeader(HTTPHeaders *headers, const HTTPHeader header);
bool setHeader(HTTPHeaders *headers, const HTTPHeader header);
bool removeHeaderByIdx(HTTPHeaders *headers, const unsigned long index);
char *getHeaderValueByKey(const HTTPHeaders *headers, const char *key);
bool removeHeaderByKey(HTTPHeaders *headers, const char *key);
unsigned long getContentLength(const HTTPHeaders *headers);
bool setContentLength(HTTPHeaders *headers, const unsigned long length);
char *getContentType(const HTTPHeaders *headers);
bool setContentType(HTTPHeaders *headers, char *type);



// request.c
typedef struct {
    char *method;
    char *url;
    char *protocol;
    char *path;
    char *queryString;
    HTTPHeaders headers;
    bool _useFileBody;
    char *_binaryBody;
    FILE *_fileBody;
    unsigned long _bodySize;
    unsigned long _currentBodyPos;
} HTTPRequest;
HTTPRequest newEmptyRequest();
bool makeEmptyRequest(HTTPRequest *req);
void freeHTTPRequest(HTTPRequest *request);
bool recvHTTPRequestFromSocket(const SOCKET client, HTTPRequest *httpRequest);
void seekRequestBody(HTTPRequest *httpRequest, const unsigned long offset);
size_t readBodyFromRequest(const HTTPRequest *httpRequest, char *buffer, const size_t bufferSize);



// response.c
typedef struct {
    HTTPStatusCode code;
    HTTPHeaders headers;
    bool _useFileBody;
    char *_binaryBody;
    FILE *_fileBody;
    unsigned long _bodySize;
} HTTPResponse;
HTTPResponse newEmptyResponse();
void freeHTTPResponse(HTTPResponse *res);
bool makeEmptyResponse(HTTPResponse *res);
bool makeTextResponse(HTTPResponse *response, char* text, unsigned long textSize);
bool makeJSONResponse(HTTPResponse *response, char* json, unsigned long jsonSize);
bool makeFileResponse(HTTPResponse *response, FILE* file);
bool makeFilePathResponse(HTTPResponse *response, const char *filePath);



// mime.c
const char* guessContentType(const char* filename);



// router.c
typedef void (*HTTPRequestHandler)(HTTPRequest*, HTTPResponse*);
typedef struct {
    const char *method;
    const char *path;
    bool fullMatch;
    const HTTPRequestHandler handler;
} Route;



// app.c
typedef void (*SocketHandler)(SOCKET);
void runHTTPServer(
    const SOCKET serverSocket, Route *routes,
    const size_t routeCount,
    const unsigned int minThreadCount,
    const unsigned int maxThreadCount
);


// handlerUtil.c
void handleStaticFile(const char *filePath, const HTTPRequest *request, HTTPResponse *response);
void handleStaticResourceDir(const char *resDir, const HTTPRequest *request, HTTPResponse *response);